﻿using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;

namespace O1.Kernel.Storage.Transactions
{
    [ContractVerification(true)]
    internal sealed class ConcurrentScope : IDisposable
    {
        public readonly long TransactionId;

        private bool closed;

        public ConcurrentScope()
        {
            this.TransactionId = MultiVersion.Next();
        }

        public List<TxRecord> WrittenVertices { get; private set; }

        public List<TxRecord> WrittenProperties { get; private set; }

        public bool Invalid { get; private set; }

        public void NotifyVertexWritten(long id, long owningTxId)
        {
            this.ThrowIfClosed();
            if (this.WrittenVertices == null)
            {
                this.WrittenVertices = new List<TxRecord>();
            }

            this.WrittenVertices.Add(new TxRecord(id, owningTxId));
            if (owningTxId != this.TransactionId)
            {
                this.Invalid = true;
            }
        }

        public void NotifyPropertiesWritten(long id, long owningTxId)
        {
            this.ThrowIfClosed();
            if (this.WrittenProperties == null)
            {
                this.WrittenProperties = new List<TxRecord>();
            }

            this.WrittenProperties.Add(new TxRecord(id, owningTxId));
            if (owningTxId != this.TransactionId)
            {
                this.Invalid = true;
            }
        }

        public void Invalidate()
        {
            // TODO: The transaction ID that collided is
            // available for error information and tracking.
            this.Invalid = true;
        }

        public void Close()
        {
            if (!this.closed)
            {
                this.TransactionId.AsCommitted().Retire();
                this.closed = true;
            }
        }

        public void Dispose()
        {
            this.Close();
        }

        private void ThrowIfClosed()
        {
            if (this.closed)
            {
                throw new InvalidOperationException("The scope is closed and data modifications are no longer allowed.");
            }
        }
    }
}